#include "wingbot_boot.h"
#include "wingbot_queue.h"
#include "crc32.h"

/**
  * @brief  校验队列中的数据
  * @param  que:        队列
            boot_file:  保存本次文件信息
  * @retval 0-成功，非0-校验不通过
  */
u8 boot_file_validate(Queue_T* que, BootFile_T* boot_file)
{
    u8 ret = 0;
    u32 crc_check = 0;
    u8* pchar = que->parr;
    boot_file->header[0] = *(pchar++);
    boot_file->header[1] = *(pchar++);
    /* 校验帧头 */
    if(boot_file->header[0] == BT_FRAME_HEAD)
    {
        if(boot_file->header[1] == BT_FRAME_FILE_INFO)
        {
            boot_file->len = *(u16*)pchar;
            if (boot_file->len % 4 == 0)
            {
                pchar += 2;
                boot_file->pos = *(u32*)pchar;
                pchar += 4;
                crc_check = crc32(pchar, boot_file->len);
                boot_file->crc = *(u32*)(pchar + boot_file->len);
                if(boot_file->crc == crc_check)
                {
                    boot_file->pdata = pchar;
                }
                else
                {
                    ret = BT_ERROR_CRC;
                    goto exit;
                }
            }
            else
            {
                ret = BT_ERROR_SIZE;
                goto exit;
            }
        }
        else if(boot_file->header[1] == 0x01)
        {
            boot_file->totalSize = *(u32*)pchar;
            pchar += 4;
            boot_file->pageNumber = *(u16*)pchar;
            pchar += 2;
            boot_file->version = *(u16*)pchar;
        }
    }
    else
    {
        ret = BT_ERROR_HEAD;
        goto exit;
    }
    
    exit:
    return ret;
}

/**
* @param len: 总字节数
*/
FLASH_Status boot_transfer_flash(uint32_t to_dest, uint32_t from_src, uint32_t len)
{
    uint32_t i = 0;
    uint32_t addr = to_dest;
    volatile FLASH_Status ret = FLASH_COMPLETE;
    
#if defined(STM32F10X_MD)
    uint32_t page_amount = (len & 0xFFFFFC00) >> 10;
    /* 计算总共要擦除多少页 */
    if(len & 0x3FF)page_amount+=1;
#endif
    
    FLASH_UnlockBank1();
    FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
    /* 擦除相关页 */
    for(i=0;i<page_amount;i++)
    {
        /* 擦除 */
        ret = FLASH_ErasePage(to_dest + i*FLASH_PAGE_SIZE);
        if(ret!=FLASH_COMPLETE)goto finish;
    }
    /* 写入 */
    while((addr < to_dest+len) && (ret == FLASH_COMPLETE))
    {
        ret = FLASH_ProgramWord(addr, get_flash_data(from_src+ addr - to_dest));
        addr += 4;
    }
    
finish:
    FLASH_LockBank1();
    return ret;
}

/**
  * @brief  把收到的数据存入片上flash的download区
  * @param  boot_file:文件控制块
  * @retval 
  */
FLASH_Status boot_download_flash(BootFile_T boot_file)
{
    u32 address = 0;
    volatile FLASH_Status ret = FLASH_COMPLETE;
    FLASH_UnlockBank1();
    /* Define the number of page to be erased */
    /* Clear All pending flags */
    FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);    
    /* Erase the FLASH page */
    ret = FLASH_ErasePage(BT_DOWNLOAD_START_ADDR + boot_file.pos);
    /* Program Flash Bank1 */
    address = BT_DOWNLOAD_START_ADDR+ boot_file.pos;
    while((address < BT_DOWNLOAD_START_ADDR + boot_file.pos + boot_file.len) && (ret == FLASH_COMPLETE))
    {
        ret = FLASH_ProgramWord(address, *(u32*)boot_file.pdata);
        address += 4;
        boot_file.pdata += 4;
    }
    FLASH_LockBank1();
    return ret;
}

/**
  * @brief 写入boot文件信息-4字节的versionInfo
  * @param  flash_addr:         写入地址
            boot_file:          boot文件控制块
  * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG,
  *         FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
*/
FLASH_Status boot_write_info(uint32_t flash_addr, BootFile_T boot_file)
{
    volatile FLASH_Status ret = FLASH_COMPLETE;
    FLASH_UnlockBank1();
    FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
    ret = FLASH_ErasePage(flash_addr);
    ret = FLASH_ProgramWord(flash_addr, boot_file.version);     //先存入版本信息
    ret = FLASH_ProgramWord(flash_addr+4, boot_file.totalSize);     //再存入文件总大小
    FLASH_LockBank1();
    return ret;
}

/**
  * @brief  读取指定地址的数据
  * @retval 该地址下的数据
*/
__STATIC_INLINE uint32_t get_flash_data(uint32_t addr)
{
    return *(__IO uint32_t*) addr;
}

/**
  * @brief  判断栈顶地址是否合法
  * @retval flash地址
  * @retval WB_NULL: 合法
*/
uint8_t boot_check_flash_begin(uint32_t appxaddr)
{
    if((get_flash_data(appxaddr)&0x2FFE0000) == 0x20000000)
        return 0;
    else
        return 0xFF;
}

static iapfun jump2app; 

__asm void MSR_MSP(u32 addr) 
{
    MSR MSP, r0             //set Main Stack value
    BX r14
}

void iap_load_app(u32 appxaddr)
{
    if(boot_check_flash_begin(appxaddr) == WB_NULL) //检查栈顶地址是否合法
    { 
        jump2app = (iapfun)*(vu32*)(appxaddr+4);    //第二个字节为程序起始地址
        MSR_MSP(*(vu32*)appxaddr);                  //栈顶地址
        jump2app();                                 //
    }
}
